﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PayloadTracker.APRS
{
    /// <summary>
    /// Sample APRS provider that performs a Live Replay from GPGGA / GPRMC sentence file.
    /// </summary>
    //TODO: Add APRS support as well for the parsed files. 
    //TODO: Add buffered processing for replay of extremely long / data rich flights.
    [Serializable]
    [SetupControlAttribute(typeof(NMEATextProviderSetup))]//Bind the class to the NMEATextProviderSetup control for setup in the new flight dialog.
    public class NMEATextProvider : IAPRSProvider
    {
        float m_Multiplier = 1.0f;
        string m_File;
        string m_Callsign = "REPLAY";


        List<APRSBeacon> m_Beacons = new List<APRSBeacon>();
        List<APRSBeacon>.Enumerator m_Iterator;
        APRSBeacon m_LastBeacon = null;
        DateTime m_LastTick;
        bool m_Enabled = false;
        bool m_Finished = false;
        
        public bool Finished
        {
            get { return m_Finished; }
        }
        public bool Enabled
        {
            get { return m_Enabled;  }
        }
        public float TimeMultiplier
        {
            get { return m_Multiplier; }
        }
        public string ReplayFile
        {
            get { return m_File; }
        }
        public string Callsign
        {
            get { return m_Callsign; }
        }

        /// <summary>
        /// private constructor used for serialization and configuration. DO NOT USE.
        /// </summary>
        private NMEATextProvider()
            : base("Replay APRS Provider")
        {

        }
        public NMEATextProvider(string callsign, string file, float timemultiplier)
            : base("Replay APRS Provider")
        {
            Initialize(callsign, file, timemultiplier);
        }


        private void Initialize(string callsign, string file, float timemultiplier)
        {
            if (timemultiplier <= 0.0f)
                throw new ArgumentException("argument cannot be less than or equal to zero.", "timemultiplier");

            m_Callsign = callsign;

            m_Multiplier = timemultiplier;

            m_File = file;

            var sr = System.IO.File.OpenText(file);

            //APRS.Parsers.GPGGA gga = new PayloadTracker.APRS.Parsers.GPGGA();
            //APRS.Parsers.GPRMC rmc = new PayloadTracker.APRS.Parsers.GPRMC();
            APRS.Parsers.NMEA nmea = new PayloadTracker.APRS.Parsers.NMEA();
            APRS.Parsers.APRSPlain aprs = new PayloadTracker.APRS.Parsers.APRSPlain();

            while (!sr.EndOfStream)
            {

                string s = sr.ReadLine();


                //APRSBeacon completebeacon = new APRSBeacon(m_Callsign);
                //bool isValid = false;                                    
                if (s == "" || s == null)
                {
                    Logger.LOG.Error("blank line: " + s);
                }
                else if (nmea.IsValidSentence(s))
                {
                    try
                    {
                        var beacon = nmea.Parse(s);
                        if (beacon == null)
                            continue;
                        else
                            beacon.Callsign = m_Callsign;

                        //completebeacon.Time = partialbeacon.Time;
                        //completebeacon.Position = partialbeacon.Position;
                        //completebeacon.Altitude = partialbeacon.Altitude;

                        // isValid = true;

                        m_Beacons.Add(beacon);
                    }
                    catch (System.Exception e)
                    {
                        //isValid = false;
                        Logger.LOG.Error(string.Format("Could not parse NMEA sentence: {0} due to {1} might be partial.", s, e));

                    }

                }
                else if (aprs.IsValidSentence(s))
                {
                    APRSBeacon beacon = null;
                    try
                    {
                        beacon = aprs.Parse(s);
                    }
                    catch (System.Exception e)
                    {
                        //isValid = false;
                        Logger.LOG.Error(string.Format("Could not parse APRS sentence: {0} due to {1}", s, e));
                    }

                    if (beacon != null)
                        m_Beacons.Add(beacon);

                }
                else
                    Logger.LOG.Error("Sentence is neither NMEA nor APRS: " + s);



            }
            this.Name = "REPLAY: " + this.ToString();
        }

        public override void Start()
        {
            m_Enabled = true;

            if(m_Iterator.Current == null)
                m_Iterator = m_Beacons.GetEnumerator();

        }

        public override void Stop()
        {
            m_Enabled = false;
        }

        public override void Tick()
        {
            if (!m_Enabled)
                return;

            if (m_LastBeacon == null)
            {
                Logger.LOG.WriteLine("Starting replay.");
                m_Iterator.MoveNext();
                m_LastBeacon = m_Iterator.Current;
                m_LastTick = DateTime.Now;
                OnAprsUpdate(m_LastBeacon);

            }
            else
            {
                    
                if (DateTime.Now.Ticks - m_LastTick.Ticks >= (m_Iterator.Current.Time.Ticks - m_LastBeacon.Time.Ticks)  / m_Multiplier)
                {
                    m_LastTick = DateTime.Now;
                    m_LastBeacon = m_Iterator.Current;


                    OnAprsUpdate(m_LastBeacon);

                    //end of flight?
                    if (!m_Iterator.MoveNext())
                    {
                        m_Enabled = false;
                        m_Finished = true;

                        Logger.LOG.WriteLine("Replay finished.");
                    }


                }
            }
        }

        public override string ToString()
        {
            return m_File + " " + m_Multiplier.ToString()+"x";
        }
        #region IXmlSerializable Members

        public override void ReadXml(System.Xml.XmlReader reader)
        {

            string file = null;
            string callsign = null;
            float speed = 0;

           while(reader.MoveToNextAttribute())
           {
               switch(reader.Name.ToLower())
               {
                   case "file":
                       {
                           file = reader.Value;
                           break;
                       }
                   case  "speed":
                       {
                           speed = float.Parse(reader.Value);
                           break;
                       }
                   case "callsign":
                       {
                           callsign = reader.Value;
                           break;
                       }
                   case "name":
                       {
                           this.Name = reader.Value;
                           break;
                       }
               }
           }

           reader.Read();

           if (file == null || callsign == null || speed == 0)
               throw new System.Xml.XmlException("Missing attribute.");


           Initialize(callsign,file,speed);
        }

        public override void WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteAttributeString("Name", this.Name);
            writer.WriteAttributeString("Callsign", this.Callsign);
            writer.WriteAttributeString("File", m_File);
            writer.WriteAttributeString("Speed", m_Multiplier.ToString());
        }

        #endregion
    }
}
